package cn.opencodes.framework.core.service;

import java.util.List;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

import cn.opencodes.framework.core.log.LogsHandle;
import cn.opencodes.framework.core.redis.RedisLock;
import cn.opencodes.framework.core.redis.RedisUtils;
import cn.opencodes.framework.core.utils.AppUtils;
import cn.opencodes.framework.core.vo.AccessLog;
import cn.opencodes.framework.core.vo.MaliciousLog;
import cn.opencodes.framework.core.vo.OperationLog;
import cn.opencodes.framework.core.vo.SerialNoConfig;
import cn.opencodes.framework.core.vo.UserRoot;
import cn.opencodes.framework.tools.utils.IDUtils;
import cn.opencodes.framework.tools.utils.StringUtils;
/**
 * 核心框架基础服务
 * @author HJ
 */
public abstract class AbstractAlphaService extends LogsHandle implements AlphaService {
	/** 权限缓存key */
	private static final String CACHE_PERMS_KEY = "shiro:perms:key:";
	/** 开发者缓存key */
	private static final String CACHE_DEV_KEY = "shiro:dev:key:";
	/** 用户信息key */
	private static final String CACHE_USER_KEY = "shiro:user:key:";
	/** 有效期1小时 */
	private static final long ONE_HOURS_EXPIRE = 60*60;
	/** 流水号key */
	private static final String SERIALNO_KEY = "SerialNo:key:";
	
	@Autowired
	private RedisUtils redis;
	
	/**
	 * 加载用户权限(系统启动时加载)
	 * @param userId 用户ID
	 */
	public abstract Set<String> doLoadPermissions(long userId);
	/**
	 * 加载用户信息(系统接收客户端每次请求是都会调用此方法)
	 * @param userId 用户ID
	 */
	public abstract UserRoot doLoadUserRootInfo(long userId);
	/**
	 * 加载开发者秘钥盐值
	 * @param clientAppKey 公钥
	 */
	public abstract String doLoadAppDeveloperSalt(String clientAppKey);
	/**
	 * 加载流水号配置表信息
	 * @param buscode 业务代码
	 */
	public abstract SerialNoConfig doLoadSerialNoCofnig(String buscode);
	/**
	 * 处理访问日志-持久化
	 * @param logs 访问日志
	 */
	public abstract void doAccessLogHandle(List<AccessLog> logs);
	/**
	 * 恶意拦截日志-持久化
	 * @param logs 恶意拦截
	 */
	public abstract void doMaliciousHandle(List<MaliciousLog> logs);
	/**
	 * 系统操作日志-持久化
	 * @param logs 系统操作
	 */
	public abstract void doOperationHandle(List<OperationLog> logs);
	
	
	/**
	 * 实现获取用户权限
	 * @param userId 用户ID
	 */
	@SuppressWarnings("unchecked")
	@Override
	public Set<String> getUserPermissions(long userId) {
		//缓存读取
		String key = buildUserPermissionsCacheKey(userId);
		Set<String> permsSet = redis.get(key, Set.class);
		if (permsSet != null) {
			return permsSet;
		}
		//读取用户权限列表
		permsSet = doLoadPermissions(userId);
		if (permsSet != null) {
			//存入缓存中
			redis.set(key, permsSet, ONE_HOURS_EXPIRE);
		}
		return permsSet;
	}
	/**
	 * 实现清空用户权限缓存
	 * @param userId 用户ID
	 */
	@Override
	public void clearUserPermissionsCache(long userId){
		String key = buildUserPermissionsCacheKey(userId);
		redis.delete(key);
	}
	
	
	/**
	 * 实现获取用户信息
	 * @param userId 用户ID
	 */
	@Override
	public UserRoot getUserRootInfo(long userId){
		//缓存读取
		String key = buildUserRootInfoCacheKey(userId);
		UserRoot ur = redis.get(key, UserRoot.class);;
		if (ur != null) {
			return ur;
		}
		//读取用户信息
		ur = doLoadUserRootInfo(userId);
		if (ur != null) {
			//存入缓存中
			redis.set(key, ur, ONE_HOURS_EXPIRE);
		}
		return ur;
	}
	/**
	 * 实现清空用户信息缓存
	 * @param userId 用户ID
	 */
	@Override
	public void clearUserRootInfoCache(long userId){
		String key = buildUserRootInfoCacheKey(userId);
		redis.delete(key);
	}
	
	
	/**
	 * 实现校验客户端公钥、秘钥
	 * @param clientAppKey 		公钥
	 * @param clientAppSecret 	秘钥
	 */
	@Override
	public boolean checkAppKeyAndSecret(String clientAppKey, String clientAppSecret) {
		//缓存读取
		String key = buildAppKeyCacheKey(clientAppKey);
		String salt = redis.get(key, String.class);
		if (StringUtils.isEmpty(salt)) {
			return false;
		}
		//读取
		salt = doLoadAppDeveloperSalt(clientAppKey);
		if (StringUtils.isNotBlank(salt)) {
			//存入缓存中
			redis.set(key, salt, ONE_HOURS_EXPIRE);
			String serverAppSecret = AppUtils.genAppSecret(clientAppKey, salt);
			return serverAppSecret.equals(clientAppSecret);
		}
		return false;
	}
	/**
	 * 清空秘钥
	 * @param clientAppKey 公钥
	 */
	@Override
	public void clearAppDeveloperSaltCache(String clientAppKey){
		String key = buildAppKeyCacheKey(clientAppKey);
		redis.delete(key);
	}
	
	
	/**
	 * 获取流水号
	 * @param buscode 业务代码
	 */
	@Override
	@RedisLock(value="#p0", keepMills=10000)
	public String getSerialNo(String buscode){
		//缓存读取
		String key = buildSerialNoCacheKey(buscode);
		SerialNoConfig cfg = redis.get(key, SerialNoConfig.class);
		if (cfg != null) {
			//返回-1表示没有可用的流水号
			if (cfg.getNo()>cfg.getMaxNo() && cfg.getOverride()==0) {
				cfg.setNo(-1);
			}
			//表示缓存已经没有可用，需要数据库更新
			if (cfg.getCurrentNo()>=cfg.getMaxNo() || cfg.getCurrentNo()%cfg.getStep()==0) {
				cfg = null;
			}
		}
		//从数据库读取流水号配置
		if (cfg == null) {
			cfg = doLoadSerialNoCofnig(buscode);
		}
		// 返回表示找不到流水号规则配置
		if (cfg == null) {
			return "0";
		}
		// 获取自增长序列号：返回-1表示没有可用的流水号
		if (cfg.getNo() == -1) {
			return "-1";
		}
		cfg.setCurrentNo(1);
		// 没有超过最大数值，从缓存获取
		int len = String.valueOf(cfg.getMaxNo()).length();
		String serialNo = IDUtils.buildDateLineNo(cfg.getPrefix(), cfg.getFmt(), cfg.getCurrentNo(), len);
		// 更新缓存
		redis.set(key, cfg, ONE_HOURS_EXPIRE);
		return serialNo;
	}
	/**
	 * 清空流水号配置缓存
	 * @param buscode 业务代码
	 */
	@Override
	public void clearSerialNoCache(String buscode){
		String key = buildSerialNoCacheKey(buscode);
		redis.delete(key);
	}
	
	
	//==========================构建缓存key===================================//
	public String buildAppKeyCacheKey(String clientAppKey){
		return CACHE_DEV_KEY+clientAppKey;
	}
	
	public String buildUserRootInfoCacheKey(long userId){
		return CACHE_USER_KEY+userId;
	}
	
	public String buildUserPermissionsCacheKey(long userId){
		return CACHE_PERMS_KEY+userId;
	}
	
	public String buildSerialNoCacheKey(String buscode){
		return SERIALNO_KEY+buscode;
	}
	
	
	//==========================日志处理===================================//
	// 日志收集
	@Override
	public <T> void collect(T log, Class<T> requiredType){
		super.collectLog(log);
	}
	// 关闭日志
	@Override
    public void closeLog(){
    	super.closeLog();
    }
	// 处理日志
	@Override
    public void logsHandle(List<AccessLog> alogs, List<MaliciousLog> mlogs, List<OperationLog> ologs){
        // 处理各日志集合
        if (!alogs.isEmpty()) {
			doAccessLogHandle(alogs);
		}
        if (!mlogs.isEmpty()) {
			doMaliciousHandle(mlogs);
		}
        if (!ologs.isEmpty()) {
			doOperationHandle(ologs);
		}
    }

}
